// Copyright 2008-2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
//
// Contains PolicyStatus class to launch a process using a COM interface. This
// COM object can be created by medium integrity callers.

#ifndef OMAHA_GOOPDATE_POLICY_STATUS_H__
#define OMAHA_GOOPDATE_POLICY_STATUS_H__

#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlcomtime.h>
#include <atlstr.h>
#include <atltime.h>
#include "omaha/base/atlregmapex.h"
#include "omaha/base/const_object_names.h"
#include "omaha/base/constants.h"
#include "omaha/base/debug.h"
#include "omaha/base/logging.h"
#include "omaha/base/omaha_version.h"
#include "omaha/base/preprocessor_fun.h"
#include "omaha/common/config_manager.h"
#include "omaha/common/const_cmd_line.h"
#include "omaha/common/const_goopdate.h"
#include "omaha/common/goopdate_utils.h"
#include "omaha/goopdate/com_proxy.h"
#include "omaha/goopdate/non_localized_resource.h"

#if defined(HAS_DEVICE_MANAGEMENT)
#include "omaha/goopdate/dm_client.h"
#endif

// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
#include "goopdate/omaha3_idl.h"

namespace omaha {

const TCHAR* const kPolicyStatusDescription =
    _T("Google Update Policy Status Class");

template <typename T>
class ATL_NO_VTABLE PolicyStatus
    : public CComObjectRootEx<CComMultiThreadModel>,
      public CComCoClass<PolicyStatus<T>>,
      public IDispatchImpl<IPolicyStatus,
                           &__uuidof(IPolicyStatus),
                           &CAtlModule::m_libid,
                           kMajorTypeLibVersion,
                           kMinorTypeLibVersion>,
      public IDispatchImpl<IPolicyStatus3,
                           &__uuidof(IPolicyStatus3),
                           &CAtlModule::m_libid,
                           kMajorTypeLibVersion,
                           kMinorTypeLibVersion>,
      public StdMarshalInfo {
 public:
  DECLARE_NOT_AGGREGATABLE(PolicyStatus)
  DECLARE_PROTECT_FINAL_CONSTRUCT()

  DECLARE_REGISTRY_RESOURCEID_EX(T::registry_res_id())

  BEGIN_REGISTRY_MAP()
    REGMAP_ENTRY(_T("HKROOT"), T::hk_root())
    REGMAP_ENTRY(_T("VERSION"), _T("1.0"))
    REGMAP_ENTRY(_T("PROGID"), T::prog_id())
    REGMAP_ENTRY(_T("DESCRIPTION"),  kPolicyStatusDescription)
    REGMAP_ENTRY(_T("CLSID"), T::class_id())
    REGMAP_MODULE2(_T("MODULE"), kOmahaOnDemandFileName)
    REGMAP_ENTRY(_T("ICONRESID"), PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON))
    REGMAP_ENTRY(_T("STRINGRESID"),
                 PP_STRINGIZE(IDS_ELEVATION_MONIKER_DISPLAYNAME))
  END_REGISTRY_MAP()

  BEGIN_COM_MAP(PolicyStatus)
    COM_INTERFACE_ENTRY(IPolicyStatus)
    COM_INTERFACE_ENTRY(IPolicyStatus2)
    COM_INTERFACE_ENTRY(IPolicyStatus3)
    COM_INTERFACE_ENTRY(IStdMarshalInfo)
  END_COM_MAP()

  PolicyStatus() : StdMarshalInfo(T::is_machine()) {
    CORE_LOG(L6, (_T("[PolicyStatus]")));
  }

  virtual ~PolicyStatus() {
    CORE_LOG(L6, (_T("[~PolicyStatus]")));
  }

  // IPolicyStatus/IPolicyStatus2.
  STDMETHODIMP get_lastCheckPeriodMinutes(DWORD* minutes) {
    ASSERT1(minutes);

    bool is_overridden = false;
    int period = ConfigManager::Instance()->GetLastCheckPeriodSec(
        &is_overridden,
        NULL);
    *minutes = static_cast<DWORD>(period) / kSecPerMin;
    return S_OK;
  }

  STDMETHODIMP get_updatesSuppressedTimes(
      DWORD* start_hour,
      DWORD* start_min,
      DWORD* duration_min,
      VARIANT_BOOL* are_updates_suppressed) {
    ASSERT1(start_hour);
    ASSERT1(start_min);
    ASSERT1(duration_min);
    ASSERT1(are_updates_suppressed);

    UpdatesSuppressedTimes times;
    bool updates_suppressed = false;

    HRESULT hr = ConfigManager::Instance()->GetUpdatesSuppressedTimes(
        CTime::GetCurrentTime(),
        &times,
        &updates_suppressed,
        NULL);
    if (FAILED(hr)) {
      return hr;
    }

    *start_hour = times.start_hour;
    *start_min = times.start_min;
    *duration_min = times.duration_min;
    *are_updates_suppressed = updates_suppressed ? VARIANT_TRUE : VARIANT_FALSE;

    return S_OK;
  }

  STDMETHODIMP get_downloadPreferenceGroupPolicy(BSTR* pref) {
    ASSERT1(pref);

    *pref = ConfigManager::Instance()->GetDownloadPreferenceGroupPolicy(NULL)
                .AllocSysString();
    return S_OK;
  }

  STDMETHODIMP get_packageCacheSizeLimitMBytes(DWORD* limit) {
    ASSERT1(limit);

    *limit = static_cast<DWORD>(
        ConfigManager::Instance()->GetPackageCacheSizeLimitMBytes(NULL));
    return S_OK;
  }

  STDMETHODIMP get_packageCacheExpirationTimeDays(DWORD* days) {
    ASSERT1(days);

    *days = static_cast<DWORD>(
        ConfigManager::Instance()->GetPackageCacheExpirationTimeDays(NULL));
    return S_OK;
  }

  STDMETHODIMP get_effectivePolicyForAppInstalls(BSTR app_id, DWORD* policy) {
    ASSERT1(policy);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    *policy = static_cast<DWORD>(
        ConfigManager::Instance()->GetEffectivePolicyForAppInstalls(app_guid,
                                                                    NULL));

    return S_OK;
  }

  STDMETHODIMP get_effectivePolicyForAppUpdates(BSTR app_id, DWORD* policy) {
    ASSERT1(policy);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    *policy = static_cast<DWORD>(
        ConfigManager::Instance()->GetEffectivePolicyForAppUpdates(app_guid,
                                                                   NULL));

    return S_OK;
  }

  STDMETHODIMP get_targetVersionPrefix(BSTR app_id, BSTR* prefix) {
    ASSERT1(prefix);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    *prefix = ConfigManager::Instance()->GetTargetVersionPrefix(app_guid, NULL)
                  .AllocSysString();

    return S_OK;
  }

  STDMETHODIMP get_isRollbackToTargetVersionAllowed(
      BSTR app_id,
      VARIANT_BOOL* rollback_allowed) {
    ASSERT1(rollback_allowed);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    *rollback_allowed =
        ConfigManager::Instance()->IsRollbackToTargetVersionAllowed(app_guid,
                                                                    NULL) ?
            VARIANT_TRUE :
            VARIANT_FALSE;

    return S_OK;
  }

  STDMETHODIMP get_updaterVersion(BSTR* version) {
    ASSERT1(version);

    *version = CString(GetVersionString()).AllocSysString();
    return S_OK;
  }

  STDMETHODIMP get_lastCheckedTime(DATE* last_checked) {
    ASSERT1(last_checked);

    const DWORD value =
        ConfigManager::Instance()->GetLastCheckedTime(T::is_machine());
    if (!value) {
      return E_FAIL;
    }

    FILETIME ft = {};
    Time64ToFileTime(Int32ToTime64(value), &ft);
    *last_checked = ATL::COleDateTime(ft);
    return S_OK;
  }

  STDMETHODIMP refreshPolicies() {
#if defined(HAS_DEVICE_MANAGEMENT)
    return T::is_machine() && ::IsUserAnAdmin() ? dm_client::RefreshPolicies() :
                                                  E_ACCESSDENIED;
#else
    return E_NOTIMPL;
#endif  // #if defined(HAS_DEVICE_MANAGEMENT)
  }

  STDMETHODIMP get_lastCheckPeriodMinutes(IPolicyStatusValue** value) {
    ASSERT1(value);

    bool is_overridden = false;

    // |value| will be returned in minutes.
    int period =
        ConfigManager::Instance()->GetLastCheckPeriodSec(&is_overridden,
                                                         value);
    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_updatesSuppressedTimes(
      IPolicyStatusValue** value,
      VARIANT_BOOL* are_updates_suppressed) {
    ASSERT1(value);
    ASSERT1(are_updates_suppressed);

    UpdatesSuppressedTimes times;
    bool updates_suppressed = false;

    HRESULT hr = ConfigManager::Instance()->GetUpdatesSuppressedTimes(
        CTime::GetCurrentTime(),
        &times,
        &updates_suppressed,
        value);
    if (FAILED(hr)) {
      return hr;
    }

    ASSERT1(*value);
    *are_updates_suppressed = updates_suppressed ? VARIANT_TRUE : VARIANT_FALSE;

    return S_OK;
  }

  STDMETHODIMP get_downloadPreferenceGroupPolicy(IPolicyStatusValue** value) {
    ASSERT1(value);

    ConfigManager::Instance()->GetDownloadPreferenceGroupPolicy(value);
    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_packageCacheSizeLimitMBytes(IPolicyStatusValue** value) {
    ASSERT1(value);

    ConfigManager::Instance()->GetPackageCacheSizeLimitMBytes(value);
    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_packageCacheExpirationTimeDays(IPolicyStatusValue** value) {
    ASSERT1(value);

    ConfigManager::Instance()->GetPackageCacheExpirationTimeDays(value);
    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_proxyMode(IPolicyStatusValue** value) {
    ASSERT1(value);

    CString proxy_mode;

    HRESULT hr = ConfigManager::Instance()->GetProxyMode(&proxy_mode, value);
    if (FAILED(hr)) {
      return hr;
    }

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_proxyPacUrl(IPolicyStatusValue** value) {
    ASSERT1(value);

    CString proxy_pac_url;

    HRESULT hr = ConfigManager::Instance()->GetProxyPacUrl(&proxy_pac_url,
                                                           value);
    if (FAILED(hr)) {
      return hr;
    }

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_proxyServer(IPolicyStatusValue** value) {
    ASSERT1(value);

    CString proxy_server;

    HRESULT hr = ConfigManager::Instance()->GetProxyServer(&proxy_server,
                                                           value);
    if (FAILED(hr)) {
      return hr;
    }

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_effectivePolicyForAppInstalls(BSTR app_id,
                                                 IPolicyStatusValue** value) {
    ASSERT1(value);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    ConfigManager::Instance()->GetEffectivePolicyForAppInstalls(app_guid,
                                                                value);

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_effectivePolicyForAppUpdates(BSTR app_id,
                                                IPolicyStatusValue** value) {
    ASSERT1(value);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    ConfigManager::Instance()->GetEffectivePolicyForAppUpdates(app_guid, value);

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_targetVersionPrefix(BSTR app_id,
                                       IPolicyStatusValue** value) {
    ASSERT1(value);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    ConfigManager::Instance()->GetTargetVersionPrefix(app_guid, value);

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_isRollbackToTargetVersionAllowed(
      BSTR app_id,
      IPolicyStatusValue** value) {
    ASSERT1(value);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    ConfigManager::Instance()->IsRollbackToTargetVersionAllowed(app_guid,
                                                                value);

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_targetChannel(BSTR app_id, IPolicyStatusValue** value) {
    ASSERT1(value);

    GUID app_guid = GUID_NULL;
    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
    if (FAILED(hr)) {
      return hr;
    }

    ConfigManager::Instance()->GetTargetChannel(app_guid, value);

    ASSERT1(*value);
    return S_OK;
  }

  STDMETHODIMP get_forceInstallApps(VARIANT_BOOL is_machine,
                                    IPolicyStatusValue** value) {
    ASSERT1(value);

    std::vector<CString> app_ids;
    HRESULT hr = ConfigManager::Instance()->GetForceInstallApps(!!is_machine,
                                                                &app_ids,
                                                                value);
    if (FAILED(hr)) {
      return hr;
    }

    ASSERT1(*value);
    return S_OK;
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(PolicyStatus);
};

struct PolicyStatusModeUser {
  static bool is_machine() { return false; }
  static const TCHAR* prog_id() { return kProgIDPolicyStatusUser; }
  static GUID class_id() { return __uuidof(PolicyStatusUserClass); }
  static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; }
  static const TCHAR* hk_root() { return _T("HKCU"); }
};

struct PolicyStatusModeMachineFallback {
  static bool is_machine() { return true; }
  static const TCHAR* prog_id() {
    return kProgIDPolicyStatusMachineFallback;
  }
  static GUID class_id() { return __uuidof(PolicyStatusMachineFallbackClass); }
  static UINT registry_res_id() { return IDR_LOCAL_SERVER_ELEVATION_RGS; }
  static const TCHAR* hk_root() { return _T("HKLM"); }
};

struct PolicyStatusModeService {
  static bool is_machine() { return true; }
  static const TCHAR* prog_id() { return kProgIDPolicyStatusSvc; }
  static GUID class_id() { return __uuidof(PolicyStatusMachineServiceClass); }
  static UINT registry_res_id() { return IDR_LOCAL_SERVICE_RGS; }
  static const TCHAR* hk_root() { return _T("HKLM"); }
};

typedef PolicyStatus<PolicyStatusModeUser> PolicyStatusUser;
typedef PolicyStatus<PolicyStatusModeMachineFallback>
    PolicyStatusMachineFallback;
typedef PolicyStatus<PolicyStatusModeService> PolicyStatusService;

}  // namespace omaha

#endif  // OMAHA_GOOPDATE_POLICY_STATUS_H__
